﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using huqiang.Collections;

namespace huqiang.Communication
{
    public class DataPack32
    {
        //[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        internal struct MsgRecvCache:IClear,IDisposable
        {
            public UInt32 msgID;
            public Int32 state;
            public int rcvPart;
            public int AllPart;
            public long lastTime;
            public byte[][] Data;

            public void Clear()
            {
                msgID = 0;
                state = 0;
                Data = null;
            }

            public void Dispose()
            {
                msgID = 0;
                state = 0;
                if (Data != null)
                {
                    for (int i = 0; i < Data.Length; i++)
                    {
                        if (Data[i] != null)
                            RecycleBuffer(Data[i]);
                    }
                    RecycleBuffer2(Data);
                }
                Data = null;
            }
        }
        static Stack<byte[]> stack1600 = new Stack<byte[]>();
        static Stack<byte[]> stack1024 = new Stack<byte[]>();
        static Stack<byte[]> stack512 = new Stack<byte[]>();
        static Stack<byte[]> stack256 = new Stack<byte[]>();
        static Stack<byte[][]> stack2 = new Stack<byte[][]>();
        static SpinLock spin = new SpinLock();
        internal static byte[] GetBuffer(int len)
        {
            bool gotLock = false;
            try
            {
                spin.Enter(ref gotLock);
                if (len > 512)
                {
                    if (len > 1024)
                    {
                        if (stack1600.Count > 0)
                            return stack1600.Pop();
                        return new byte[1600];
                    }
                    else
                    {
                        if (stack1024.Count > 0)
                            return stack1024.Pop();
                        return new byte[1024];
                    }
                }
                else if (len > 256)
                {
                    if (stack512.Count > 0)
                        return stack512.Pop();
                    return new byte[512];
                }
                if (stack256.Count > 0)
                    return stack256.Pop();
                return new byte[256];
            }
            finally
            {
                if (gotLock)
                    spin.Exit();
            }
        }
        internal static void RecycleBuffer(byte[] buf)
        {
            bool gotLock = false;
            try
            {
                spin.Enter(ref gotLock);
                int len = buf.Length;
                if (len > 512)
                {
                    if (len > 1024)
                    {
                        stack1600.Push(buf);
                    }
                    else
                    {
                        stack1024.Push(buf);
                    }
                }
                else if (len > 256)
                {
                    stack512.Push(buf);
                }
                else
                {
                    stack256.Push(buf);
                }
            }
            finally
            {
                if (gotLock)
                    spin.Exit();
            }
        }
        internal static byte[][] GetBuffer2()
        {
            bool gotLock = false;
            try
            {
                spin.Enter(ref gotLock);
                if (stack2.Count > 0)
                    return stack2.Pop();
                return new byte[32][];
            }
            finally
            {
                if (gotLock)
                    spin.Exit();
            }
        }
        internal static void RecycleBuffer2(byte[][] buf)
        {
            bool gotLock = false;
            try
            {
                spin.Enter(ref gotLock);
                stack2.Push(buf);
            }
            finally
            {
                if (gotLock)
                    spin.Exit();
            }
        }
        public static void ClearBuffer()
        {
            stack1600.Clear();
            stack1024.Clear();
            stack512.Clear();
            stack256.Clear();
            stack2.Clear();
        }

        public static int PackInt32(byte[] src, int slen, ref byte[] tar, int index = 0)
        {
            int p = slen / 124;
            int r = slen % 124;
            if (r > 0)
                p++;
            int len = p * 4 + slen;
            if (tar == null)
                tar = GetBuffer(len + index + 8);// new byte[len + 8];
            tar[index] = 253;
            tar[index + 1] = 255;
            tar[index + 2] = 255;
            tar[index + 3] = 255;
            int o = index + len + 4;
            tar[o] = 254;
            o++;
            tar[o] = 255;
            o++;
            tar[o] = 255;
            o++;
            tar[o] = 255;
            int s = 0;
            int t = index + 4;
            for (int j = 0; j < p; j++)
            {
                int a = 0;
                int st = t;
                t += 4;
                for (int i = 0; i < 31; i++)
                {
                    byte b = src[s];
                    if (b > 127)
                    {
                        a |= 1 << i;
                        b -= 128;
                    }
                    tar[t] = b;
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = src[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = src[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = src[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                }
                unsafe
                {
                    fixed (byte* bp = &tar[st])
                        *(int*)bp = a;
                }
                byte w = tar[st];
                tar[st] = tar[st + 3];
                tar[st + 3] = w;
            }
            return len + 8;
        }
        public static byte[] PackInt32(byte[] buf, int slen, out int olen,byte[] tar = null)
        {
            int p = slen / 124;
            int r = slen % 124;
            if (r > 0)
                p++;
            int len = p * 4 + slen;
            olen = len + 8;
            if (tar == null)
                tar = GetBuffer(olen);// new byte[len + 8];
            tar[0] = 253;
            tar[1] = 255;
            tar[2] = 255;
            tar[3] = 255;
            int o = len + 4;
            tar[o] = 254;
            o++;
            tar[o] = 255;
            o++;
            tar[o] = 255;
            o++;
            tar[o] = 255;
            int s = 0;
            int t = 4;
            for (int j = 0; j < p; j++)
            {
                int a = 0;
                int st = t;
                t += 4;
                for (int i = 0; i < 31; i++)
                {
                    byte b = buf[s];
                    if (b > 127)
                    {
                        a |= 1 << i;
                        b -= 128;
                    }
                    tar[t] = b;
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = buf[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = buf[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                    tar[t] = buf[s];
                    t++; s++;
                    if (s >= slen)
                        break;
                }
                unsafe
                {
                    fixed (byte* bp = &tar[st])
                        *(int*)bp = a;
                }
                byte w = tar[st];
                tar[st] = tar[st + 3];
                tar[st + 3] = w;
            }
            return tar;
        }
        static void UnpackInt(byte[] src, int si, int len, byte[] buf, int ti)
        {
            byte w = src[si];
            src[si] = src[si + 3];
            src[si + 3] = w;
            int a = 0;// src[si];
            unsafe
            {
                fixed (byte* bp = &src[0])
                    a = *(Int32*)(bp + si);
            }
            int st = si + 4;
            int tt = ti;
            for (int i = 0; i < 124; i++)
            {
                if (tt >= len)
                    break;
                buf[tt] = src[st];
                st++;
                tt++;
            }
            si += 4;
            for (int i = 0; i < 31; i++)
            {
                if (ti >= len)
                    break;
                byte b = src[si];
                if ((a & 1) > 0)
                    b += 128;
                a >>= 1;
                buf[ti] = b;
                ti += 4;
                si += 4;
            }
        }
        public static int UnpackInt32(byte[] src, int start, int end, byte[] tar)
        {
            int len = end - start;
            int part = len / 128;
            int r = len % 128;
            int tl = part * 124;
            if (r > 0)
            {
                part++;
                tl += r;
                tl -= 4;
            }
            int s0 = start;
            int s1 = 0;
            for (int i = 0; i < part; i++)
            {
                UnpackInt(src, s0, tl, tar, s1);
                s0 += 128;
                s1 += 124;
            }
            return tl;
        }
        public static bool GetPart(byte[] buf, ref int start, ref int end)
        {
            int s = start;
            int len = end - start;
            for (int i = 0; i < len; i++)
            {
                var b = buf[s];
                if (b == 253)
                {
                    if (s + 3 < end)
                    {
                        if (buf[s + 1] == 255)
                            if (buf[s + 2] == 255)
                                if (buf[s + 3] == 255)
                                {
                                    start = s + 4;
                                    s += 3;
                                }
                    }
                }
                else if (b == 254)
                {
                    if (s + 3 < end)
                    {
                        if (buf[s + 1] == 255)
                            if (buf[s + 2] == 255)
                                if (buf[s + 3] == 255)
                                {
                                    end = s;
                                    return true;
                                }
                    }
                }
                s++;
            }
            return false;
        }
        public static bool GetPartStart(byte[] buf, ref int start, int end)
        {
            int s = start;
            int len = end - start;
            for (int i = 0; i < len; i++)
            {
                var b = buf[s];
                if (b == 253)
                {
                    if (s + 3 < end)
                    {
                        if (buf[s + 1] == 255)
                            if (buf[s + 2] == 255)
                                if (buf[s + 3] == 255)
                                {
                                    start = s;
                                    return true;
                                }
                    }
                }
            }
            return false;
        }
        public static bool GetPartEnd(byte[] buf, int start, ref int end)
        {
            int s = start;
            int len = end - start;
            for (int i = 0; i < len; i++)
            {
                var b = buf[s];
                if (b == 254)
                {
                    if (s + 3 < end)
                    {
                        if (buf[s + 1] == 255)
                            if (buf[s + 2] == 255)
                                if (buf[s + 3] == 255)
                                {
                                    end = s;
                                    return true;
                                }
                    }
                }
            }
            return false;
        }
    }
}
